"""项目成员管理服务"""
from __future__ import annotations

from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple

import logging
from bson import ObjectId
from motor.motor_asyncio import AsyncIOMotorDatabase

from app.core.permissions import Permissions
from app.models.project import ProjectMemberRole
from app.schemas.team_member import (
    TeamMemberCreateRequest,
    TeamMemberListResponse,
    TeamMemberProjectInfo,
    TeamMemberRecord,
    TeamMemberStatistics,
    TeamMemberUpdateRequest,
    TeamMemberUserInfo,
)
from app.services.role_service import RoleService


logger = logging.getLogger(__name__)


class TeamMemberService:
    """封装项目成员管理相关业务逻辑"""

    async def list_team_members(
        self,
        db: AsyncIOMotorDatabase,
        current_user: dict,
        *,
        project_id: Optional[str] = None,
        role: Optional[str] = None,
        status: Optional[str] = None,
        keyword: Optional[str] = None,
        page: int = 1,
        size: int = 20,
        sort_by: str = "joined_at",
        sort_order: int = -1,
    ) -> TeamMemberListResponse:
        """分页获取团队成员列表并附带统计信息"""

        if size <= 0:
            raise ValueError("size must be greater than 0")

        skip = (page - 1) * size if page > 0 else 0

        user_permissions = set(current_user.get("permissions", []))
        current_user_id = current_user.get("user_id")

        # 非管理员、无全局项目读取权限的用户仅能看到自己可访问的项目
        match_conditions: List[Dict[str, Any]] = []

        user_object_id: Optional[ObjectId] = None
        if current_user_id and ObjectId.is_valid(current_user_id):
            user_object_id = ObjectId(current_user_id)

        if project_id:
            if not ObjectId.is_valid(project_id):
                raise ValueError("Invalid project_id format")
            match_conditions.append({"_id": ObjectId(project_id)})

        if Permissions.ADMIN not in user_permissions and Permissions.PROJECT_READ_ALL not in user_permissions:
            if user_object_id is None:
                raise PermissionError("Invalid current user identifier")
            match_conditions.append(
                {
                    "$or": [
                        {"is_public": True},
                        {"created_by": user_object_id},
                        {"members.user_id": user_object_id},
                    ]
                }
            )

        pipeline: List[Dict[str, Any]] = []
        if match_conditions:
            if len(match_conditions) == 1:
                pipeline.append({"$match": match_conditions[0]})
            else:
                pipeline.append({"$match": {"$and": match_conditions}})

        pipeline.extend([
            {"$unwind": "$members"},
        ])

        member_filters: List[Dict[str, Any]] = []
        if role:
            # 尝试通过代码查找角色
            role_doc = await db.roles.find_one({"code": role})
            if role_doc:
                member_filters.append({"members.roles": str(role_doc["_id"])})
            elif ObjectId.is_valid(role):
                member_filters.append({"members.roles": role})
            else:
                # 角色不存在，返回空结果
                return TeamMemberListResponse(items=[], total=0, page=page, size=size, statistics=TeamMemberStatistics())
                
        if member_filters:
            pipeline.append({"$match": {"$and": member_filters}})

        pipeline.extend(
            [
                {
                    "$lookup": {
                        "from": "users",
                        "localField": "members.user_id",
                        "foreignField": "_id",
                        "as": "member_user",
                    }
                },
                {"$unwind": "$member_user"},
            ]
        )

        if status:
            pipeline.append({"$match": {"member_user.status": status}})

        if keyword:
            regex = {"$regex": keyword, "$options": "i"}
            pipeline.append(
                {
                    "$match": {
                        "$or": [
                            {"member_user.username": regex},
                            {"member_user.name": regex},
                            {"member_user.email": regex},
                            {"name": regex},
                        ]
                    }
                }
            )

        pipeline.append(
            {
                "$project": {
                    "project_id": {"$toString": "$_id"},
                    "project_name": "$name",
                    "project_status": "$status",
                    "project_priority": "$priority",
                    "project_is_archived": {"$ifNull": ["$is_archived", False]},
                    "member_id": {"$toString": "$member_user._id"},
                    "member_username": "$member_user.username",
                    "member_name": "$member_user.name",
                    "member_email": "$member_user.email",
                    "member_avatar": "$member_user.avatar",
                    "member_status": "$member_user.status",
                    "member_department": "$member_user.department",
                    "member_position": "$member_user.position",
                    "roles": "$members.roles",
                    "permissions": {"$ifNull": ["$members.permissions", []]},
                    "joined_at": "$members.joined_at",
                }
            }
        )

        sort_field_map = {
            "joined_at": "joined_at",
            "project_name": "project_name",
            "member_name": "member_name",
            "member_status": "member_status",
        }
        sort_field = sort_field_map.get(sort_by, "joined_at")
        sort_direction = 1 if sort_order and sort_order >= 0 else -1
        pipeline.append({"$sort": {sort_field: sort_direction, "project_id": 1, "member_id": 1}})

        pipeline.append(
            {
                "$facet": {
                    "items": [
                        {"$skip": skip},
                        {"$limit": size},
                    ],
                    "total": [{"$count": "count"}],
                    "status_stats": [
                        {"$group": {"_id": "$member_status", "count": {"$sum": 1}}},
                    ],
                    "role_stats": [
                        {"$unwind": "$roles"},
                        {"$group": {"_id": "$roles", "count": {"$sum": 1}}},
                    ],
                    "project_stats": [
                        {
                            "$group": {
                                "_id": "$project_id",
                                "name": {"$first": "$project_name"},
                                "count": {"$sum": 1},
                            }
                        },
                        {"$sort": {"count": -1}},
                    ],
                }
            }
        )

        aggregate_result = await db.projects.aggregate(pipeline).to_list(length=1)
        if not aggregate_result:
            return TeamMemberListResponse(
                items=[],
                total=0,
                page=page,
                size=size,
                statistics=TeamMemberStatistics(),
            )

        facet = aggregate_result[0]

        total_list = facet.get("total", [])
        total = total_list[0]["count"] if total_list else 0

        # 收集所有角色ID，批量查询角色信息
        all_role_ids: List[str] = []
        for doc in facet.get("items", []):
            roles = doc.get("roles") or []
            if isinstance(roles, list):
                all_role_ids.extend([str(rid) for rid in roles if rid])
        
        # 批量查询角色信息
        role_map: Dict[str, Dict[str, Any]] = {}
        if all_role_ids:
            unique_role_ids = list(dict.fromkeys(all_role_ids))
            role_object_ids = []
            for rid in unique_role_ids:
                if ObjectId.is_valid(rid):
                    role_object_ids.append(ObjectId(rid))
            
            if role_object_ids:
                role_cursor = db.roles.find({"_id": {"$in": role_object_ids}}, {"name": 1, "code": 1})
                role_docs = await role_cursor.to_list(length=None)
                for role_doc in role_docs:
                    role_id = str(role_doc["_id"])
                    role_map[role_id] = {
                        "name": role_doc.get("name", ""),
                        "code": role_doc.get("code", "")
                    }

        items: List[TeamMemberRecord] = []
        for doc in facet.get("items", []):
            roles = doc.get("roles") or []
            if not isinstance(roles, list):
                roles = []

            # 获取角色名称和代码
            role_names: List[str] = []
            role_codes: List[str] = []
            for role_id in roles:
                role_id_str = str(role_id)
                if role_id_str in role_map:
                    role_info = role_map[role_id_str]
                    if role_info.get("name"):
                        role_names.append(role_info["name"])
                    if role_info.get("code"):
                        role_codes.append(role_info["code"])

            permissions = doc.get("permissions") or []
            joined_at = doc.get("joined_at") or datetime.utcnow()

            project_info = TeamMemberProjectInfo(
                id=doc.get("project_id"),
                name=doc.get("project_name", ""),
                status=doc.get("project_status"),
                priority=doc.get("project_priority"),
                is_archived=bool(doc.get("project_is_archived", False)),
            )

            member_info = TeamMemberUserInfo(
                id=doc.get("member_id"),
                username=doc.get("member_username"),
                name=doc.get("member_name"),
                email=doc.get("member_email"),
                avatar=doc.get("member_avatar"),
                status=doc.get("member_status"),
                department=doc.get("member_department"),
                position=doc.get("member_position"),
            )

            items.append(
                TeamMemberRecord(
                    project=project_info,
                    member=member_info,
                    roles=[str(rid) for rid in roles],
                    role_names=role_names,
                    role_codes=role_codes,
                    permissions=list(dict.fromkeys(permissions)),
                    joined_at=joined_at,
                )
            )

        status_counts = {entry["_id"]: entry["count"] for entry in facet.get("status_stats", []) if entry.get("_id")}
        
        # 处理角色统计：将角色ID转换为角色代码
        # 复用已查询的 role_map，如果统计中的角色ID不在 role_map 中，再查询
        role_counts: Dict[str, int] = {}
        role_stats_entries = facet.get("role_stats", [])
        if role_stats_entries:
            # 收集统计中出现的角色ID，检查哪些不在已查询的 role_map 中
            role_ids_for_stats = [str(entry["_id"]) for entry in role_stats_entries if entry.get("_id")]
            missing_role_ids = [rid for rid in role_ids_for_stats if rid not in role_map]
            
            # 如果有缺失的角色ID，补充查询
            if missing_role_ids:
                role_object_ids_for_stats = []
                for rid in missing_role_ids:
                    if ObjectId.is_valid(rid):
                        role_object_ids_for_stats.append(ObjectId(rid))
                
                if role_object_ids_for_stats:
                    role_cursor_for_stats = db.roles.find({"_id": {"$in": role_object_ids_for_stats}}, {"code": 1, "name": 1})
                    role_docs_for_stats = await role_cursor_for_stats.to_list(length=None)
                    for role_doc in role_docs_for_stats:
                        role_id_str = str(role_doc["_id"])
                        role_map[role_id_str] = {
                            "name": role_doc.get("name", ""),
                            "code": role_doc.get("code", "")
                        }
            
            # 将角色ID统计转换为角色代码统计
            for entry in role_stats_entries:
                role_id = str(entry["_id"]) if entry.get("_id") else None
                if role_id and role_id in role_map:
                    role_info = role_map[role_id]
                    role_code = role_info.get("code", "")
                    if role_code:
                        count = entry.get("count", 0)
                        # 如果同一个角色代码出现多次（不应该发生），累加计数
                        role_counts[role_code] = role_counts.get(role_code, 0) + count
        
        project_counts = [
            {
                "project_id": entry.get("_id"),
                "project_name": entry.get("name"),
                "count": entry.get("count", 0),
            }
            for entry in facet.get("project_stats", [])
        ]

        statistics = TeamMemberStatistics(
            total_members=total,
            active_members=status_counts.get("active", 0),
            inactive_members=status_counts.get("inactive", 0),
            by_role=role_counts,
            by_project=project_counts,
        )

        return TeamMemberListResponse(
            items=items,
            total=total,
            page=page,
            size=size,
            statistics=statistics,
        )

    async def add_team_member(
        self,
        db: AsyncIOMotorDatabase,
        current_user: dict,
        payload: TeamMemberCreateRequest,
    ) -> None:
        """添加项目成员"""

        project, project_object_id = await self._get_project(db, payload.project_id)
        user, user_object_id = await self._get_user(db, payload.user_id)

        if not self._has_project_permission(
            project,
            current_user,
            {Permissions.PROJECT_MANAGE_MEMBERS, Permissions.PROJECT_ADD_MEMBER},
        ):
            raise PermissionError("Not enough permissions to add project member")

        if any(member.get("user_id") == user_object_id for member in project.get("members", [])):
            raise ValueError("User is already a project member")

        # 处理角色：支持角色ID列表或角色代码
        role_ids: List[str] = []
        if payload.roles:
            # 如果提供了角色ID列表，直接使用
            role_ids = payload.roles
        elif payload.role:
            # 如果提供了角色代码，查询对应的角色ID
            role_doc = await db.roles.find_one({"code": payload.role, "scope": "project"})
            if role_doc:
                role_ids = [str(role_doc["_id"])]
            else:
                raise ValueError(f"Role with code '{payload.role}' not found")
        else:
            raise ValueError("Either 'roles' or 'role' must be provided")

        member_doc = {
            "user_id": user_object_id,
            "roles": role_ids,
            "joined_at": datetime.utcnow(),
            "permissions": list(dict.fromkeys(payload.permissions or [])),
        }

        await db.projects.update_one(
            {"_id": project_object_id},
            {
                "$push": {"members": member_doc},
                "$set": {"updated_at": datetime.utcnow()},
            },
        )

        logger.info(
            "Added user %s to project %s by %s",
            str(user_object_id),
            str(project_object_id),
            current_user.get("user_id"),
        )

    async def update_team_member(
        self,
        db: AsyncIOMotorDatabase,
        current_user: dict,
        project_id: str,
        user_id: str,
        payload: TeamMemberUpdateRequest,
    ) -> None:
        """更新项目成员信息"""

        project, project_object_id = await self._get_project(db, project_id)
        _, user_object_id = await self._get_user(db, user_id)

        if not self._has_project_permission(
            project,
            current_user,
            {Permissions.PROJECT_MANAGE_MEMBERS, Permissions.PROJECT_UPDATE_MEMBER_ROLE},
        ):
            raise PermissionError("Not enough permissions to update project member")

        if not any(member.get("user_id") == user_object_id for member in project.get("members", [])):
            raise ValueError("Member not found in project")

        update_fields: Dict[str, Any] = {}
        data = payload.model_dump(exclude_unset=True)
        
        # 处理角色：支持角色ID列表或角色代码
        if "role" in data and data["role"] is not None:
            # 如果提供了角色代码，查询对应的角色ID
            role_doc = await db.roles.find_one({"code": data["role"], "scope": "project"})
            if role_doc:
                update_fields["members.$.roles"] = [str(role_doc["_id"])]
            else:
                raise ValueError(f"Role with code '{data['role']}' not found")
        elif "roles" in data and data["roles"] is not None:
            # 如果提供了角色ID列表，直接使用
            update_fields["members.$.roles"] = data["roles"]
        
        # 处理权限
        if "permissions" in data and data["permissions"] is not None:
            update_fields["members.$.permissions"] = list(dict.fromkeys(data["permissions"]))

        if not update_fields:
            return

        await db.projects.update_one(
            {"_id": project_object_id, "members.user_id": user_object_id},
            {
                "$set": {
                    **update_fields,
                    "updated_at": datetime.utcnow(),
                }
            },
        )

        logger.info(
            "Updated project member %s in project %s by %s",
            str(user_object_id),
            str(project_object_id),
            current_user.get("user_id"),
        )

    async def remove_team_member(
        self,
        db: AsyncIOMotorDatabase,
        current_user: dict,
        project_id: str,
        user_id: str,
    ) -> None:
        """移除项目成员"""

        project, project_object_id = await self._get_project(db, project_id)
        _, user_object_id = await self._get_user(db, user_id)

        if not self._has_project_permission(
            project,
            current_user,
            {Permissions.PROJECT_MANAGE_MEMBERS, Permissions.PROJECT_REMOVE_MEMBER},
        ):
            raise PermissionError("Not enough permissions to remove project member")

        if not any(member.get("user_id") == user_object_id for member in project.get("members", [])):
            raise ValueError("Member not found in project")

        await db.projects.update_one(
            {"_id": project_object_id},
            {
                "$pull": {"members": {"user_id": user_object_id}},
                "$set": {"updated_at": datetime.utcnow()},
            },
        )

        logger.info(
            "Removed user %s from project %s by %s",
            str(user_object_id),
            str(project_object_id),
            current_user.get("user_id"),
        )

    async def _get_project(
        self, db: AsyncIOMotorDatabase, project_id: str
    ) -> Tuple[Dict[str, Any], ObjectId]:
        if not ObjectId.is_valid(project_id):
            raise ValueError("Invalid project ID format")
        object_id = ObjectId(project_id)
        project = await db.projects.find_one({"_id": object_id})
        if not project:
            raise ValueError("Project not found")
        return project, object_id

    async def _get_user(
        self, db: AsyncIOMotorDatabase, user_id: str
    ) -> Tuple[Dict[str, Any], ObjectId]:
        if not ObjectId.is_valid(user_id):
            raise ValueError("Invalid user ID format")
        object_id = ObjectId(user_id)
        user = await db.users.find_one({"_id": object_id})
        if not user:
            raise ValueError("User not found")
        return user, object_id

    def _has_project_permission(
        self,
        project: Dict[str, Any],
        current_user: dict,
        required_permissions: set[str],
    ) -> bool:
        user_permissions = set(current_user.get("permissions", []))
        if Permissions.ADMIN in user_permissions:
            return True

        if user_permissions.intersection(required_permissions):
            return True

        current_user_id = current_user.get("user_id")
        if current_user_id and ObjectId.is_valid(current_user_id):
            user_object_id = ObjectId(current_user_id)
            if project.get("created_by") == user_object_id:
                return True

            for member in project.get("members", []):
                if (
                    member.get("user_id") == user_object_id
                    and "project:manage_members" in member.get("permissions", [])
                ):
                    return True

        return False


team_member_service = TeamMemberService()



